using System;
using System.Text.RegularExpressions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using SolarWinds.Helpers;
using SolarWinds.Input;

namespace SolarWinds.Widgets
{
    public enum TextAlignment
    {
        Left,
        Right,
        Center
    }

    public class TextBox: TiledWidget
    {
        public event EventHandler EnterPressed;

        private void InvokeEnterPressed(EventArgs e)
        {
            EventHandler handler = EnterPressed;
            if (handler != null) handler(this, e);
        }

        enum ColorStates
        {
            Hover,
            Focus,
            Pressed,
            Released
        }

        private const double cBlinkSpeed = 400.0f; // milliseconds
        private double mCursorTime = 0.0f;
        private bool mIsCursorVisible = false;

        private ColorStates mState = ColorStates.Released;
        private bool mStateChangedFlag;
        
        private string mValue = "";
        public string Value
        {
            get { return mValue; }
            set 
            { 
                if (mValue != value)
                {
                    if (mValidationExpression == "" ||
                        Regex.Match(value, mValidationExpression).Success)
                    {
                        mValue = value;
                    }
                }
            }
        }

        private TextAlignment mTextAlignment = TextAlignment.Left;
        public TextAlignment TextAlignment
        {
            get { return mTextAlignment; }
            set { mTextAlignment = value; }
        }

        private string mValidationExpression = "";

        public string ValidationExpression
        {
            get { return mValidationExpression; }
            set { mValidationExpression = value; }
        }

        public TextBox(Game game) : 
            base(game)
        {
        }

        protected internal override void PaintEvent()
        {
            base.PaintEvent();

            // strip string
            String stripValue = SpriteFontHelper.StripString(Font, Value, Width - 2 * mTileSize.X);
            
            // compute text position
            Vector2 fontSize = (stripValue == "") ? Font.MeasureString("|") : Font.MeasureString(stripValue);
            Vector2 pos;
            switch (mTextAlignment)
            {
                case TextAlignment.Left:
                    pos = new Vector2(ScreenPosition.X + 16,
                                      ScreenPosition.Y + (Height - fontSize.Y) * 0.5f);
                    break;
                case TextAlignment.Right:
                    pos = new Vector2(ScreenPosition.X + (Width - fontSize.X - 16),
                                      ScreenPosition.Y + (Height - fontSize.Y) * 0.5f);
                    break;
                    default:
                    pos = new Vector2(ScreenPosition.X + (Width - fontSize.X) * 0.5f,
                                      ScreenPosition.Y + (Height - fontSize.Y) * 0.5f);
                    break;
            }

            if (mIsCursorVisible)
            {
                stripValue += "|";
            }

            SpriteBatch.DrawString(Font, stripValue, pos, TextColor);
        }

        protected override void InitializeRects()
        {
            mSrcRects = new Rectangle[3];
            mDestRects = new Rectangle[3];
            mTileColors = new Color[3] { Color.White, Color.White, Color.White };

            MinWidth = 2 * mTileSize.X;
            MinHeight = mTileSize.Y;
        }

        protected override void UpdateSrcDestRects()
        {
            // left
            mSrcRects[0] = TileIndex2Rectangle(new Point(4, 2));
            mDestRects[0] = new Rectangle(ScreenPosition.X, ScreenPosition.Y, mTileSize.X, Height);
            // middle
            mSrcRects[1] = TileIndex2Rectangle(new Point(5, 2));
            mDestRects[1] = new Rectangle(ScreenPosition.X + mTileSize.X, ScreenPosition.Y, Width - 2 * mTileSize.X, Height);
            // right
            mSrcRects[2] = TileIndex2Rectangle(new Point(6, 2));
            mDestRects[2] = new Rectangle(ScreenPosition.X - mTileSize.X + Width, ScreenPosition.Y, mTileSize.X, Height);
        }

        // TODO move to base class
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            if (mStateChangedFlag)
            {
                switch (mState)
                {
                    case ColorStates.Hover:
                        mTileColors[0] = Color.CadetBlue;
                        mTileColors[1] = Color.CadetBlue;
                        mTileColors[2] = Color.CadetBlue;
                        break;
                    case ColorStates.Focus:
                        mTileColors[0] = Color.CornflowerBlue;
                        mTileColors[1] = Color.CornflowerBlue;
                        mTileColors[2] = Color.CornflowerBlue;
                        break;
                    case ColorStates.Pressed:
                        mTileColors[0] = Color.Gray;
                        mTileColors[1] = Color.Gray;
                        mTileColors[2] = Color.Gray;
                        break;
                    case ColorStates.Released:
                        mTileColors[0] = Color.White;
                        mTileColors[1] = Color.White;
                        mTileColors[2] = Color.White;
                        break;
                    default:
                        break;
                }


                mStateChangedFlag = false;
            }

            // blinking Cursor
            if (mState == ColorStates.Focus)
            {
                if (mCursorTime < 0.0f)
                {
                    mIsCursorVisible = !mIsCursorVisible;
                    mCursorTime = cBlinkSpeed;
                }
                else
                {
                    mCursorTime -= gameTime.ElapsedGameTime.TotalMilliseconds;
                }
            }
        }

        protected internal override void MousePressEvent(MouseEvent evt)
        {
            mState = ColorStates.Pressed;
            mStateChangedFlag = true;

            base.MousePressEvent(evt);
        }

        protected internal override void MouseReleaseEvent(MouseEvent evt)
        {
            if (IsMouseOver)
            {
                mState = ColorStates.Hover;
            }
            else if (HasKeyFocus)
            {
                mState = ColorStates.Focus;
            }
            else
            {
                mState = ColorStates.Released;
            }

            mStateChangedFlag = true;

            base.MouseReleaseEvent(evt);
        }

        protected internal override void MouseOverEvent(MouseEvent evt)
        {
            mState = ColorStates.Hover;
            mStateChangedFlag = true;
        }

        protected internal override void MouseOutEvent(MouseEvent evt)
        {
            mState = (HasKeyFocus) ? ColorStates.Focus : ColorStates.Released;
            mStateChangedFlag = true;
        }

        protected internal override void KeyFocusInEvent(KeyEvent evt)
        {
            mState = ColorStates.Focus;
            mStateChangedFlag = true;
        }

        protected internal override void KeyFocusOutEvent(KeyEvent evt)
        {
            mState = (IsMouseOver) ? ColorStates.Hover : ColorStates.Released;
            mStateChangedFlag = true;
        }

        protected internal override void KeyClickedEvent(KeyEvent evt)
        {
            if (evt.Key == Keys.Back)
            {
                string oldValue = Value;
                if (oldValue.Length > 0)
                {
                    Value = oldValue.Substring(0, oldValue.Length - 1); 
                }
            }
            else if (evt.Key == Keys.Enter)
            {
                InvokeEnterPressed(null);
            }
            else
            {
                Value += KeyboardManager.GetInputString(evt.Key, evt.Modifiers);    
            }
        }
    }
}